﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MyTRCP.Common.MyWave;
using NAudio.CoreAudioApi;
using NAudio.Wave;

namespace MyTRCP.Common.Services
{
    public class MicRecordService : BackgroundService
    {
        private readonly ILogger<MicRecordService> _logger;
        private readonly BlockingQueue<byte[]> _micVoiceQueue;
        private volatile bool _recordingFlag;
        private volatile bool _requestQuit;
        private volatile AutoResetEvent _event;
        private volatile MyWaveInEvent? wi;
        private volatile bool _needChangeDev;
        private volatile bool _changeSucc;
        private volatile int _newDevId;
        private volatile AutoResetEvent _changeComplete;
        private volatile int _devId;

        public BlockingQueue<byte[]> MicVoiceQueue
        {
            get
            {
                return _micVoiceQueue;
            }
        }
        public MicRecordService(ILogger<MicRecordService> logger)
        {
            logger.LogDebug($"MicRecordService creating...");
            _logger = logger;

            _micVoiceQueue = new BlockingQueue<byte[]>();
            _recordingFlag = false;
            _requestQuit = false;
            _event = new AutoResetEvent(false);
            _changeComplete = new AutoResetEvent(false);
            _devId = 0;
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Run(() => ThreadMethod(stoppingToken), stoppingToken);
        }
        public Dictionary<int, WaveInCapabilities> GetWaveInDevCapDic()
        {
            int count = MyWaveInEvent.DeviceCount;
            Dictionary<int, WaveInCapabilities> dic = new Dictionary<int, WaveInCapabilities>();
            for (int i = 0; i < count; i++)
            {
                WaveInCapabilities woc = MyWaveInEvent.GetCapabilities(i);
                dic.Add(i, woc);
            }
            return dic;
        }
        private void OnWaveInDataAvailable(object? sender, WaveInEventArgs e)
        {
            _micVoiceQueue.Add(e.Buffer);
        }
        public bool ChangeRecordDevice(int devId)
        {
            _devId = devId;
            _changeSucc = false;
            if (wi != null)
            {
                try
                {
                    MyWaveInEvent.GetCapabilities(devId);
                    _needChangeDev = true;
                    _newDevId = devId;
                    _event.Set();
                    _changeComplete.WaitOne();
                    return _changeSucc;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"ChangeRecordDevice Exception:{ex.Message}");
                }
            }
            else
            {
                return true;
            }
            return false;
        }
        public void StartRecording()
        {
            if (wi != null)
            {
                _recordingFlag = true;
                _event.Set();
                return;
            }
            _logger.LogWarning("启动录音失败");
        }
        public void StopRecording()
        {
            if (wi != null)
            {
                _recordingFlag = false;
                _event.Set();
                return;
            }
            _logger.LogWarning("停止录音失败");
        }
        private void ThreadMethod(CancellationToken stoppingToken)
        {
            _logger.LogInformation($"语音采集线程开始");
            try
            {
                wi = new MyWaveInEvent();
                wi.DeviceNumber = _devId;
                wi.DataAvailable += OnWaveInDataAvailable;
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        _event.WaitOne();
                        if (_requestQuit)
                        {
                            wi.Dispose();
                            _requestQuit = false;
                            break;
                        }
                        else if (_needChangeDev)
                        {
                            wi.DeviceNumber = _newDevId;
                            if (wi.CaptureState == CaptureState.Capturing)
                            {
                                wi.StopRecording();
                                wi.StartRecording();
                            }
                            _changeSucc = true;
                            _changeComplete.Set();
                            _needChangeDev = false;
                        }
                        else
                        {
                            if (wi.CaptureState != CaptureState.Capturing && _recordingFlag)
                            {
                                wi.StartRecording();
                            }
                            else if (wi.CaptureState != CaptureState.Stopped && !_recordingFlag)
                            {
                                wi.StopRecording();
                            }
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation("语音采集线程循环接收到OperationCanceledException,跳出.");
                        break;
                    }
                    catch (Exception e)
                    {
                        _logger.LogError(e, $"语音采集线程循环内出现异常：{e.Message}");
                        _changeSucc = false;
                        _changeComplete.Set();
                        _needChangeDev = false;
                    }
                }
                wi = null;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"语音采集线程出现异常：{ex.Message}");
                wi?.Dispose();
            }
            _logger.LogInformation($"语音采集线程结束");
        }
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("startasync called");
            _requestQuit = false;
            _event.Reset();
            return base.StartAsync(cancellationToken);
        }
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _requestQuit = true;
            _event.Set();
            return base.StopAsync(cancellationToken);
        }
    }
}
